package com.hd.trans.ui.model

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.hd.trans.db.DataBaseMgr
import com.hd.trans.db.bean.HuDunLanguage
import com.hd.trans.db.bean.TranslateRecord
import io.reactivex.Observable
import io.reactivex.ObservableEmitter
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.observers.DisposableObserver
import io.reactivex.schedulers.Schedulers
import io.reactivex.ObservableOnSubscribe
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext

open class BaseViewModel: ViewModel() {
    var isLoading: MutableLiveData<Boolean> = MutableLiveData(false)
    val toastMessage: MutableLiveData<String> = MutableLiveData()
    val languageFrom: MutableLiveData<HuDunLanguage> = MutableLiveData() // 源语言
    val languageTo: MutableLiveData<HuDunLanguage> = MutableLiveData() // 目标语言
    val detectLang: MutableLiveData<HuDunLanguage> = MutableLiveData() // 检测到的语言
    val operateRecord: MutableLiveData<TranslateRecord> = MutableLiveData() // 当前操作记录
    val listTranslateRecord: MutableLiveData<List<TranslateRecord>?> = MutableLiveData() // 历史记录



    fun refreshRecords(isVoice: Boolean) {
        Observable.create<List<TranslateRecord>?>(
            ObservableOnSubscribe<List<TranslateRecord>?> {emit: ObservableEmitter<List<TranslateRecord>?> ->
                emit.onNext(DataBaseMgr.getInstance().getAllRecords(isVoice))
                emit.onComplete()
            }).subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : DisposableObserver<List<TranslateRecord>?>() {
                override fun onNext(list: List<TranslateRecord>) {
                    listTranslateRecord.value = list
                }

                override fun onError(e: Throwable) {
                }

                override fun onComplete() {
                }
            })
    }

    private fun toast(message: String? = "") {
        message?.let {
            toastMessage.value = it
        }
    }

    val handler = CoroutineExceptionHandler { _, ex ->
        toast(RuntimeException(ex).message)
    }

    fun launch(
        context: CoroutineContext = handler,
        scope: CoroutineScope = viewModelScope,
        block: suspend CoroutineScope.() -> Unit
    ): Job {
        return scope.launch(context, block = block)
    }

    suspend fun <T> ioCall(
        loading: Boolean = false,
        toastType: ToastType = ToastType.ERROR,
        func: suspend () -> T
    ): Promise<T> {
        return call(loading, toastType) {
            withContext(Dispatchers.IO) {
                func()
            }
        }
    }

    suspend fun <T> call(
        loading: Boolean = false,
        toastType: ToastType = ToastType.ERROR,
        func: suspend () -> T
    ): Promise<T> {
        if (loading) {
            isLoading.value = true
        }
        return try {
            val data = func()
            if (loading) {
                isLoading.value = false
            }
            Promise(data, null)
        } catch (e: Throwable) {
            e.printStackTrace()
            isLoading.value = false
            val exception = if (e is RuntimeException) e else RuntimeException(e)
            when (toastType) {
                ToastType.ALL, ToastType.ERROR -> {
                    if (exception.message?.contains("java.net") == false) {
                        toast(exception.message)
                    }
                }
                else -> {
                }
            }

            Promise(null, exception)

        }
    }


}




class Promise<T> (var data:T?, private val error: RuntimeException? = null){
    fun <K> then(func:(data:T)->K) : Promise<K>{
        if(error!=null){
            return Promise(null,error)
        }
        return try {
            if(data!=null)  Promise(func(data!!),null) else Promise(null, RuntimeException(""))
        }catch (e:Throwable){
            Promise(null, RuntimeException(e))
        }
    }


    fun catch(handlerError:((e: RuntimeException) -> Unit)? = null){
        error?.let {
            handlerError?.invoke(it)
        }

    }





}
enum class ToastType{
    NONE,ERROR,ALL
}
